package com.tuananh.radnom.model.protocol.netbios;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;

import jcifs.UniAddress;
import jcifs.netbios.NbtAddress;


import com.tuananh.radnom.model.discovery.DiscoverNetInt;
import com.tuananh.radnom.model.iputils.IPFilter;
import com.tuananh.radnom.model.iputils.IPRange;
import com.tuananh.radnom.model.iputils.InetTools;
import com.tuananh.radnom.model.protocol.service.ActiveDiscover;

@XmlDiscriminatorValue("ActiveNetBios")
public class ActiveNetBios extends ActiveDiscover {
	private String getWorkgroup(String targetIP) {
		String res = "<*>";
		NbtAddress[] addrs = null;
		try {
			addrs = NbtAddress.getAllByAddress(targetIP);
			for (int jj = 0; jj < addrs.length; jj++) {
				boolean GroupAddr = false;
				GroupAddr = addrs[jj].isGroupAddress();
				if (GroupAddr) {
					res = addrs[jj].toString();
					res = res.substring(0, res.indexOf("<"));
					break;
				}
			}
		} catch (UnknownHostException ex) {
			System.out.println("Exception(getWorkgroup) is..." + ex);
			// if (errorOutput == true) {
			// System.out.println("getWorkgroup!\n");
			// Logger.getLogger(JSmbScannerView.class.getName()).log(Level.SEVERE,
			// null, ex);
			// }

		}
		return res;
	}

	public static class NetbiosInfo {
		public String dns_name = null; // FQDN
		public String nbt_name = null;
		public String work_group = null;
		public String mac = null;
		public String ip = null;
	}

	static class NetworkWorker implements Callable<NetbiosInfo> {
		public NetworkWorker(){
			
		}
		static boolean errorOutput = false;

		// Get from host his name of Workgroup

		private String getWorkgroup(String targetIP) {
			String res = "<*>";
			NbtAddress[] addrs = null;
			try {
				addrs = NbtAddress.getAllByAddress(targetIP);
				for (int jj = 0; jj < addrs.length; jj++) {
					boolean GroupAddr = false;
					GroupAddr = addrs[jj].isGroupAddress();
					if (GroupAddr) {
						res = addrs[jj].toString();
						res = res.substring(0, res.indexOf("<"));
						break;
					}
				}
			} catch (UnknownHostException ex) {
				System.out.println("Exception(getWorkgroup) is..." + ex);
				if (errorOutput == true) {
					System.out.println("getWorkgroup!\n");
					// Logger.getLogger(JSmbScannerView.class.getName()).log(Level.SEVERE,
					// null, ex);
				}

			}
			return res;
		}

		// Main thread process
		String name;
		int networkTimeout;
		String ipString;
		String result;

		NetworkWorker(String threadname, String full_ip, int timeOut) {
			name = threadname;
			networkTimeout = timeOut;
			ipString = full_ip;
		//	System.out.println(" * " + name + " has started  ");

		}

		public NetbiosInfo call() {
			NetbiosInfo netbiosInfo = new NetbiosInfo();
			try {
				InetAddress Address = InetAddress.getByName(ipString);
				// Is host reachable?
				boolean status = Address.isReachable(networkTimeout);
				// boolean status = Address.isReachable(null, 5,
				// networkTimeout);
				if (status) { // Host is accessed
					// Get DNS name
					netbiosInfo.dns_name = Address.getHostName();
					// Get NetBIOS data
					UniAddress ua = null;
					ua = UniAddress.getByName(ipString);
					NbtAddress nbt_addr = NbtAddress.getByName(ipString);
					// NBT name
					netbiosInfo.nbt_name = ua.getHostName();
					if (!netbiosInfo.nbt_name.equals(ipString)) {
						// System.out.println("NBT name =\"" + nbt_name + "\"");
						// WORKGROUP
						netbiosInfo.work_group = getWorkgroup(ipString);
						// MAC address
						byte[] array1 = nbt_addr.getMacAddress();
						netbiosInfo.mac = InetTools.MACtoString(array1);
					} else {
						netbiosInfo.work_group = null;
						netbiosInfo.mac = null;
						netbiosInfo.nbt_name = null;
					}
					netbiosInfo.ip = ipString;
				} else {
					// Host is unreacheble
					return null;
				}
			} catch (Exception ex) {
				System.out.println("I've caught exception. It is..." + ex);
				if (errorOutput) {
					// Logger.getLogger(JSmbScannerView.class.getName()).log(Level.SEVERE,
					// null, ex);
				}
			}

			return netbiosInfo;
		}
	}

	public void run() {
		System.out.println("Begin Active Netbios");
		for (IPRange range : this.getRange()) {
			for (IPFilter include : range.getIncludeIps()) {
				ExecutorService pool = Executors.newCachedThreadPool();
				Set<Future<NetbiosInfo>> set = new HashSet<Future<NetbiosInfo>>();
				long startIp, endIp;
				try {
					startIp = InetTools.AddressToNum(include.getStartIP());
					endIp = include.getEndIP() != null ? InetTools
							.AddressToNum(include.getEndIP()) : startIp;

					String full_ip = null;
					for (long i = startIp; i <= endIp; i++) {
						try {
							full_ip = InetTools.NumToAddress(i);
						} catch (UnknownHostException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						;
						Callable<NetbiosInfo> callable = new NetworkWorker(
								"Num " + i, full_ip, 5000);
						Future<NetbiosInfo> future = pool.submit(callable);
						set.add(future);
					}
					
					
					for (Future<NetbiosInfo> future : set) {
	                    // Processing information
						NetbiosInfo threadAnswer = null;
	                    try {
	                        threadAnswer = future.get(5000*3, TimeUnit.MILLISECONDS);
	                        //System.out.println("Results =" + threadAnswer);
	                    } catch (InterruptedException ex) {
	                      
	                    } catch (ExecutionException ex) {
	                       
	                    } catch (TimeoutException ex) {
	                        System.out.println("threadAnswer couldn't get result");
	                    }
	                    if (threadAnswer!=null) {
	                    //	System.out.println(threadAnswer.ip+ " "+threadAnswer.dns_name+" "+threadAnswer.nbt_name);
	                    	DiscoverNetInt discoverNetInt = new DiscoverNetInt();
	                    	if (threadAnswer.nbt_name==null){
	                    		if (threadAnswer.dns_name!=null) discoverNetInt.setNodeName(threadAnswer.dns_name);
	                    	}	               
	                    	else discoverNetInt.setNodeName(threadAnswer.nbt_name);
	                    	discoverNetInt.setIp(threadAnswer.ip);
	                    	updateObservers(discoverNetInt);
	                    }
					}
				} catch (UnknownHostException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		}
	}

	@Override
	public ArrayList<String> broadcastDiscover() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public ArrayList<String> discoverNetInt() {
		// TODO Auto-generated method stub
		run();
		return null;
	}

	@Override
	public HashMap<String, String> discoverType(ArrayList<String> discoveredIPs) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public HashMap<String, String> discoverConnection(ArrayList<String> inputIPs) {
		// TODO Auto-generated method stub
		return null;
	}

}
